# This file is part of PyMXit, a Python library implementing the basic
# functionality of the MXit protocol.
#
# Copyright (C) 2008 Ralf Kistner <ralf.kistner@gmail.com>
# 
# PyMXit is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# PyMXit is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with PyMXit.  If not, see <http://www.gnu.org/licenses/>.

from datetime import datetime
from copy import copy
import logging
import exceptions

from mxit.enum import Enumeration

logger = logging.getLogger("libmxit.handles")

BuddyType = Enumeration('BuddyType', [
    ('MXIT', 0),
    ('JABBER', 1),
    ('SERVICE', 8),
    ('GALLERY', 12),
    ('INFO', 13),
    ('ROOM', 14),
])

Presence = Enumeration('Presence', [
    ('OFFLINE', 0),
    ('AVAILABLE', 1),
    ('AWAY', 2),
    ('CHAT', 3),
    ('BUSY', 4),
    ('XA', 5),
    ('PENDING', 98),    # Buddy invited us
    ('NONE', 99),       # Not in buddy list 
    
])

Mood = Enumeration('Mood', [
    ('NONE', 0),
    ('ANGRY', 1),
    ('EXCITED', 2),
    ('GRUMPY', 3),
    ('HAPPY', 4),
    ('INLOVE', 5),
    ('INVINCIBLE', 6),
    ('SAD', 7),
    ('HOT', 8),
    ('SICK', 9),
    ('SLEEPY', 10),
])


Mood.NONE.text = ''
Mood.ANGRY.text = 'Angry'
Mood.EXCITED.text = 'Excited'
Mood.GRUMPY.text = 'Grumpy'
Mood.HAPPY.text = 'Happy'
Mood.INLOVE.text = 'In love'
Mood.INVINCIBLE.text = 'Invicible'
Mood.SAD.text = 'Sad'
Mood.HOT.text = 'Hot'
Mood.SICK.text = 'Sick'
Mood.SLEEPY.text = 'Sleepy'


MessageType = Enumeration('MessageType', [
    ('TEXT', 1),
    ('NOTICE', 2),     #eg. "xxx has left the room"
    ('COMMAND', 7),    # May contain menu's
])
              

StatusChangeReason = Enumeration('StatusChange', [
    'UNKNOWN',
    'REQUESTED',
    'NETWORK_ERROR',
    'TIMEOUT',
    'AUTH_FAILED',
    'ERROR',
])

Status = Enumeration('Status', [
    'DISCONNECTED',
    'CONNECTING',
    'AUTHENTICATING',
    'ACTIVE',
    'DISCONNECTING',
])

Subscription = Enumeration('Subscription', [
    'NONE',
    'INVITED',
    'SUBSCRIBED',
])
  
class Group:
    def __init__(self, name):
        self.name = name
        self.buddies = []
        
    def is_root(self):
        return len(self.name) == 0
    
    def __str__(self):
        return self.name
        
    def __repr__(self):
        return str(self)
    
class Buddy:
    def __init__(self, jid, name, group, type=BuddyType.MXIT, presence=Presence.NONE, mood=Mood.NONE):
        self.jid = jid
        self.name = name
        self.group = group            
        self.type = type
        self.presence = presence
        self.mood = mood
        
    def is_listed(self):
        return self.presence != Presence.NONE
    
    def is_subscribed(self):
        return self.presence != Presence.NONE and self.presence != Presence.PENDING
    
    def __eq__(self, buddy):
        return self.id == buddy.jid
        
            
            
    def __str__(self):
        return str(self.__dict__)
    
    def __repr__(self):
        return "<Buddy %s>" % str(self)
    
class Roster:
    def __init__(self):
        self.buddies = {}
        self.groups = {}
        self.self_buddy = Buddy(jid='', name='self', group=self.root_group(), presence=Presence.AVAILABLE)
        
        
    def get_group(self, name):
        if name in self.groups:
            return self.groups[name]
        else:
            self.groups[name] = Group(name)
            return self.groups[name]
        
    def root_group(self):
        return self.get_group('')
                
    def update_buddy(self, jid, **attrs):
        if jid in self.buddies:
            buddy = self.buddies[jid]
            updated = {}
            for key, value in attrs.items():
                if getattr(buddy, key) != attrs[key]:
                    updated[key] = value
                    setattr(buddy, key, value)
            return (buddy, False, updated)
        else:
            buddy = Buddy(jid=jid, **attrs)
            self.buddies[jid] = buddy
            return (buddy, True, {})
        
                
    def add_buddy(self, buddy):
        """
        Adds or updates a buddy.
        Returns True if added, False if updated
        """
        if buddy.jid in self.buddies:
            b = self.buddies[buddy.jid]
            b.name = buddy.name
            b.group = buddy.group
            b.type = buddy.type
            b.presence = buddy.presence
            b.mood = buddy.mood
            return False
        else:
            self.buddies[buddy.jid] = buddy
            return True
        
    def get_buddy(self, jid):
        jid = str(jid)
        if jid in self.buddies:
            return self.buddies[jid]
        else:
            buddy = Buddy(jid=jid, name=jid, group=self.root_group(), presence=Presence.NONE)
            self.add_buddy(buddy)
            return buddy
    
    def find_buddy(self, name):
        for jid, buddy in self.buddies.items():
            if buddy.name == name:
                return buddy
        return None
    
    def remove_buddy(self, buddy):
        del self.buddies[buddy.jid]

    def has_buddy(self, jid):
        return jid in self.buddies
    

def mtd(s):
    result = {}
    for a in s.split('|'):
        key, value = a.split('=')
        result[key] = value
    return result

class Message:
    def __init__(self, buddy, message, time=None, type=MessageType.TEXT):
        if time == None:
            time = datetime.now()
        self.buddy = buddy
        self._message = message
        self.type = type
        self.id = id
        
    @property
    def message(self):
        if self.type == MessageType.COMMAND:
            try:
                msg = self._message
                newmsg = ""
                end = 0
                while True:
                    i = msg.find('::', end)
                    if i < 0:
                        newmsg += msg[end:]
                        break
                    newmsg += msg[end:i]
                    i += 2
                    end = msg.find(':', i)
                    if end >= i:
                        d = mtd(msg[i:end])
                        if 'replymsg' in d:
                            if d['replymsg'] in d['selmsg']:
                                newmsg += d['selmsg']
                            else:
                                newmsg += d['replymsg'] + ')'
                        else:
                            newmsg += d['selmsg']
                    end += 1
                return newmsg
            except exceptions.Exception, e:
                logger.exception(e)
                return self._message
            
        else:
            return self._message
    
    @property
    def raw_message(self):
        return self._message
    
    def __str__(self):
        return "%s: %s" % (self.buddy.name, self.message)
    
    def __repr__(self):
        return "<Message %s>" % str(self)
    
class RoomMessage(Message):
    def __init__(self, room, message, sender=None, time=None, type=MessageType.TEXT):
        Message.__init__(self, room, message, time=time, type=type)
        self.sender = sender
        
        if self.sender:
            e = message.find('>\n')
            self.room_message = message[e+2:]
        else:
            self.room_message = message
        
    
    def __str__(self):
        return "%s - %s: %s" % (self.buddy.name, self.sender and self.sender.name or '', self.room_message)
    
    def __repr__(self):
        return "<RoomMessage %s>" % str(self)
    